#include "task/task_chain.h"
#include "task/context.h"
#include "logger/logger.h"
#include <vector>

namespace task
{
class TaskChainData
{
private:
    friend class TaskChain;
    scheduler::AbstractSchedulerConstPtr scheduler_;
    std::string name_;
    task::TaskChainStatus task_status_{task::TaskChainStatusIdle};
    std::vector<TaskChainStartCallback> start_callbacks_;
    std::vector<TaskChainFinishCallback> finish_callbacks_;
    std::unique_ptr<Context> task_chain_context_;
    task::TaskChainEventNotify event_notify_;
};
}

namespace task
{

TaskChain::TaskChain(const std::string &name, const scheduler::AbstractSchedulerConstPtr &scheduler)
    : d(std::make_unique<TaskChainData>())
{
    d->scheduler_ = scheduler;
    d->scheduler_->setTaskEventNotify(std::bind(&TaskChain::taskEventNotify, this, std::placeholders::_1));
    d->name_ = name;
    d->task_chain_context_ = std::make_unique<Context>();
    d->scheduler_->setContext(d->task_chain_context_.get());
    setRunnableName(name);
}
TaskChain::TaskChain(const std::string &name)
{
    d->name_ = name;
    d->task_chain_context_ = std::make_unique<Context>();
    setRunnableName(name);
}
TaskChain::~TaskChain()
{
    EASE_DEBUG("task chain destruction, name: %s ", getName().c_str());
}

std::string TaskChain::getName() const
{
    return d->name_;
}
task::TaskChainStatus TaskChain::getTaskStatus() const
{
    return d->task_status_;
}
void TaskChain::setFinishCallback(TaskChainFinishCallback callback)
{
    d->finish_callbacks_.push_back(std::move(callback));
}

void TaskChain::setStartCallback(TaskChainStartCallback callback)
{
    d->start_callbacks_.push_back(std::move(callback));
}

void TaskChain::setScheduler(const scheduler::AbstractSchedulerConstPtr &scheduler)
{
    d->scheduler_ = scheduler;
    d->scheduler_->setTaskEventNotify(std::bind(&TaskChain::taskEventNotify, this, std::placeholders::_1));
    d->scheduler_->setContext(d->task_chain_context_.get());
}
void TaskChain::start()
{
    if (nullptr == d->scheduler_)
    {
        EASE_ERROR("the scheduler in task chain is nullptr");
        return;
    }
    setTaskChainStatus(TaskChainStatus::TaskChainStatusRunning);
    d->scheduler_->run();
    setTaskChainStatus(TaskChainStatus::TaskChainStatusRelease);
}
void TaskChain::pause()
{
    if (nullptr == d->scheduler_)
    {
        EASE_ERROR("the scheduler in task chain is nullptr");
        return;
    }
    d->scheduler_->pause();
    setTaskChainStatus(TaskChainStatus::TaskChainStatusPause);
}
void TaskChain::resume()
{
    if (nullptr == d->scheduler_)
    {
        EASE_ERROR("the scheduler in task chain is nullptr");
        return;
    }
    d->scheduler_->resume();
    setTaskChainStatus(TaskChainStatus::TaskChainStatusRunning);
}
void TaskChain::cancel()
{
    if (nullptr == d->scheduler_)
    {
        EASE_ERROR("the scheduler in task chain is nullptr");
        return;
    }
    d->scheduler_->cancel();
}
void TaskChain::execFinishCallback()
{
    for (auto &callback: d->finish_callbacks_)
    {
        callback(d->name_, d->task_chain_context_.get());
    }
    EASE_DEBUG("task chain %s, callback exec done, size: %d", getName().c_str(), d->finish_callbacks_.size());
}
void TaskChain::setTaskEventNotify(task::TaskChainEventNotify event_notify)
{
    d->event_notify_ = std::move(event_notify);
}
void TaskChain::run()
{
    for (auto &callback: d->start_callbacks_)
    {
        callback(d->name_, d->task_chain_context_.get());
    }
    start();
    execFinishCallback();
    setTaskChainStatus(TaskChainStatus::TaskChainStatusIdle);
}
void TaskChain::setData(const std::string &name, std::any &value)
{
    d->task_chain_context_->setData(name, value);
}
bool TaskChain::getData(const std::string &name, std::any &value)
{
    return d->task_chain_context_->getData(name, value);
}
void TaskChain::setTaskChainStatus(task::TaskChainStatus task_chain_status)
{
    std::string status_str = "unknown status";
    switch (task_chain_status)
    {
        case TaskChainStatusIdle:
            status_str = "idle";
            break;
        case TaskChainStatusRunning:
            status_str = "running";
            break;
        case TaskChainStatusPause:
            status_str = "pause";
            break;
        case TaskChainStatusRelease:
            status_str = "release";
            break;
        default:
            break;
    }
    EASE_DEBUG("update task chain: %s, status: %s", getName().c_str(), status_str.c_str());//TODO，是否要由用户判断
    d->task_status_ = task_chain_status;
    setData("_work_ease_TASK_CHAIN_STATUS", task_chain_status);
}
void TaskChain::taskEventNotify(task::TaskEventType task_event_type)
{
    if (d->event_notify_)
    {
        d->event_notify_(getName(), task_event_type);
    }
}
}
